using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;

namespace WpfRcp.Controls.Gestures
{
    public class TapGestureBahavior
    {
        #region Fields

        private readonly UIElement m_Element;

        private readonly Dictionary<TouchDevice, TapGestureEngine> m_TapStatusCollection;
        
        #endregion

        #region Properties

        public bool HandlesTouches { get; set; }

        public int MinMilliseconds { get; set; }

        public int MaxMilliseconds { get; set; }

        public double MaxMovement { get; set; }

        #endregion

        #region Events

        public event EventHandler Tap;

        private void OnTap()
        {
            if (Tap != null)
            {
                Tap(m_Element, EventArgs.Empty);
            }
        }

        #endregion

        #region Constructors

        public TapGestureBahavior(UIElement element)
        {
            if (element == null) throw new ArgumentNullException();

            MaxMovement = 25;
            MaxMilliseconds = 1000;
            HandlesTouches = false;
            
            m_Element = element;
            m_TapStatusCollection = new Dictionary<TouchDevice, TapGestureEngine>();

            m_Element.TouchDown += OnTouchDown;
            m_Element.TouchMove += OnTouchMove;
            m_Element.TouchUp += OnTouchUp;
        }

        #endregion
        
        #region Event Handlers

        void OnTouchDown(object sender, TouchEventArgs e)
        {
            if (HandlesTouches)
            {
                e.Handled = true;
            }
            RemoveGesture(e.TouchDevice);

            var gesture = new TapGestureEngine(MinMilliseconds, MaxMilliseconds, MaxMovement);
            gesture.TouchDevice = e.TouchDevice;
            gesture.GestureAborted += OnGestureAborted;
            gesture.GestureStarted += OnGestureStarted;
            gesture.GestureCompleted += OnGestureCompleted;

            DateTime dt = TimestampToDateTime(e.Timestamp);
            Point position = e.GetTouchPoint(null).Position;

            gesture.TrackTouchDown(dt, position);
            m_TapStatusCollection.Add(e.TouchDevice, gesture);
        }

        void OnTouchMove(object sender, TouchEventArgs e)
        {
            if (HandlesTouches)
            {
                e.Handled = true;
            }
            if (!m_TapStatusCollection.ContainsKey(e.TouchDevice))
            {
                return;
            }

            Point position = e.GetTouchPoint(null).Position;

            m_TapStatusCollection[e.TouchDevice].TrackTouchMove(position);
        }

        void OnTouchUp(object sender, TouchEventArgs e)
        {
            if (HandlesTouches)
            {
                e.Handled = true;
            }
            if (!m_TapStatusCollection.ContainsKey(e.TouchDevice))
            {
                return;
            }
            DateTime dt = TimestampToDateTime(e.Timestamp);

            Point position = e.GetTouchPoint(null).Position;

            m_TapStatusCollection[e.TouchDevice].TrackTouchUp(dt, position);
        }

        private static DateTime TimestampToDateTime(int timestamp)
        {
            DateTime dt = DateTime.Now;
            dt.AddMilliseconds(Environment.TickCount - timestamp);
            return dt;
        }

        private void RemoveGesture(TouchDevice device)
        {
            if (!m_TapStatusCollection.ContainsKey(device))
            {
                return;
            }
            m_TapStatusCollection[device].AbortGesture();
            m_TapStatusCollection.Remove(device);
        }

        #endregion

        #region Gesture events

        void OnGestureCompleted(object sender, EventArgs e)
        {
            TapGestureEngine gesture = sender as TapGestureEngine;
            if (gesture == null)
                return;

            RemoveGesture(gesture.TouchDevice);

            OnTap();
        }

        void OnGestureStarted(object sender, EventArgs e)
        {
        }

        void OnGestureAborted(object sender, EventArgs e)
        {
            TapGestureEngine gesture = sender as TapGestureEngine;
            if (gesture == null)
                return;

            if (!m_TapStatusCollection.ContainsKey(gesture.TouchDevice))
            {
                return;
            }
            m_TapStatusCollection.Remove(gesture.TouchDevice);
        }

        #endregion
    }
}